//SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

use proto_gen_rust::admin_auth_api::admin_auth_api_client::AdminAuthApiClient;
use proto_gen_rust::admin_auth_api::*;
use signature::Signer;
use ssh_key::PrivateKey;
use std::time::Duration;
use tauri::async_runtime::Mutex;
use tauri::{App, Emitter};
use tauri::{AppHandle, Manager, Runtime};
use tokio::fs;
use tokio::io::AsyncReadExt;
use tokio::time::sleep;

use crate::notice_decode::new_wrong_session_notice;

#[derive(Default)]
pub struct CurAdminSession(pub Mutex<Option<String>>);

#[tauri::command]
pub async fn admin_auth_api_pre_auth<R: Runtime>(
    app_handle: AppHandle<R>,
    request: PreAuthRequest,
) -> Result<PreAuthResponse, String> {
    let chan = super::get_grpc_chan(&app_handle).await;
    if (&chan).is_none() {
        return Err("no grpc conn".into());
    }
    let mut client = AdminAuthApiClient::new(chan.unwrap());
    match client.pre_auth(request).await {
        Ok(response) => {
            let inner_resp = response.into_inner();
            return Ok(inner_resp);
        }
        Err(status) => Err(status.message().into()),
    }
}

#[tauri::command]
pub async fn admin_auth_api_auth<R: Runtime>(
    app_handle: AppHandle<R>,
    request: AuthRequest,
) -> Result<AuthResponse, String> {
    let chan = super::get_grpc_chan(&app_handle).await;
    if (&chan).is_none() {
        return Err("no grpc conn".into());
    }
    let admin_session_id = request.admin_session_id.clone();
    let mut client = AdminAuthApiClient::new(chan.unwrap());
    match client.auth(request).await {
        Ok(response) => {
            let inner_resp = response.into_inner();
            if inner_resp.code == auth_response::Code::WrongSession as i32
                || inner_resp.code == auth_response::Code::WrongSign as i32
            {
                if let Err(err) =
                    &app_handle.emit("notice", new_wrong_session_notice("auth".into()))
                {
                    println!("{:?}", err);
                }
            }
            let sess = app_handle.state::<CurAdminSession>().inner();
            *sess.0.lock().await = Some(admin_session_id);

            return Ok(inner_resp);
        }
        Err(status) => Err(status.message().into()),
    }
}

#[tauri::command]
pub async fn admin_auth_api_logout<R: Runtime>(app_handle: AppHandle<R>) {
    let sess = app_handle.state::<CurAdminSession>().inner();
    *sess.0.lock().await = None;
}

pub async fn logout<R: Runtime>(app_handle: AppHandle<R>) {
    admin_auth_api_logout(app_handle).await;
}

#[tauri::command]
pub async fn admin_auth_api_get_admin_session<R: Runtime>(app_handle: AppHandle<R>) -> String {
    let cur_value = app_handle.state::<CurAdminSession>().inner();
    let cur_session = cur_value.0.lock().await;
    if let Some(cur_session) = cur_session.clone() {
        return cur_session;
    }
    return "".into();
}

#[tauri::command]
pub async fn admin_auth_api_sign(
    private_key_file: String,
    to_sign_str: String,
) -> Result<Signature, String> {
    let f = fs::File::open(private_key_file).await;
    if f.is_err() {
        return Err(f.err().unwrap().to_string());
    }
    let mut f = f.unwrap();
    let mut private_key_data = Vec::new();
    let res = f.read_to_end(&mut private_key_data).await;
    if res.is_err() {
        return Err(res.err().unwrap().to_string());
    }
    let private_key = PrivateKey::from_openssh(private_key_data);
    if private_key.is_err() {
        return Err(private_key.err().unwrap().to_string());
    }
    let private_key = private_key.unwrap();
    let sign_res = private_key.try_sign(to_sign_str.as_bytes());
    if sign_res.is_err() {
        return Err(sign_res.err().unwrap().to_string());
    }
    let sign_res = sign_res.unwrap();
    return Ok(Signature {
        format: sign_res.algorithm().to_string(),
        blob: Vec::from(sign_res.as_bytes()),
        rest: Vec::new(),
    });
}

async fn keep_alive<R: Runtime>(handle: AppHandle<R>) {
    loop {
        sleep(Duration::from_secs(30)).await;
        let mut session_id = String::from("");
        {
            let cur_value = handle.state::<CurAdminSession>().inner();
            let cur_session = cur_value.0.lock().await;
            if let Some(cur_session) = cur_session.clone() {
                session_id.clone_from(&cur_session);
            }
        }
        if session_id != "" {
            if let Some(chan) = super::get_grpc_chan(&handle).await {
                let mut client = AdminAuthApiClient::new(chan);
                let resp = client
                    .keep_alive(KeepAliveRequest {
                        admin_session_id: session_id,
                    })
                    .await;
                if let Err(err) = resp {
                    println!("err {}", err);
                } else {
                    let inner_resp = resp.unwrap().into_inner();
                    if inner_resp.code == keep_alive_response::Code::WrongSession as i32
                        || inner_resp.code == keep_alive_response::Code::NotAuth as i32
                    {
                        admin_auth_api_logout(handle.clone()).await;
                        if let Err(err) = &handle.emit_to(
                            "main",
                            "notice",
                            new_wrong_session_notice("keep_alive".into()),
                        ) {
                            println!("{:?}", err);
                        }
                    }
                }
            }
        }
    }
}

pub fn init(app: &App) {
    app.manage(CurAdminSession(Default::default()));
    let handle = app.handle().clone();
    tauri::async_runtime::spawn(async move {
        keep_alive(handle).await;
    });
}
